#include <errno.h> /* for -EBUSY */
#include <string.h>
#include <stdio.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <time.h>
#include <pthread.h>

#include "OSSRV_Linux.h"
#include  "../OSSRV_SOCKETS/OSSRV_SOCKETS_Linux.h"
#include "../COMMON/common.h"


typedef struct timespec timespec_t; /* timespec defined in time.h */
extern int errno; 

int OSSRV_wait ( unsigned short int  msec )
{
    timespec_t  req_time;

	if (msec > 999) /* more than 1 Sec */
	{

		req_time.tv_sec  = msec / 1000;
		req_time.tv_nsec = (msec * 1000 * 1000) - ((msec / 1000)*1000*1000*1000);
	} else
	{
		req_time.tv_sec  = 0; /* Zero seconds */
		req_time.tv_nsec = (msec * 1000 * 1000);
	}
	nanosleep (&req_time,NULL);

	/* usleep(msec * 1000); previous implementation which gave worse results */

	return (EXIT_OK);
}
unsigned long OSSRV_create_thread_with_arg ( void  *thread_function, void  *arg )
{
	int result = 0;
	unsigned long  thread_id;  
	pthread_t      mythread;		

    result = pthread_create(&mythread, NULL, thread_function, arg);
	if (result != 0)
	{ /* Error creating thread */
		//PASLOG_ERROR_1(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "OSSRV Linux create_thread error - %d\n", result);
		thread_id = 0;
	} else
	{
		thread_id = (unsigned long) mythread;
	}

	return (thread_id);
}

unsigned long OSSRV_create_thread ( void  *thread_function )
{
	return (OSSRV_create_thread_with_arg(thread_function, NULL));
}

void OSSRV_terminate_thread ( void )
{
	void *return_param = NULL;
	pthread_exit(return_param);
}

int OSSRV_counting_semaphore_init ( OSSRV_counting_semaphore_t  *semaphore, 
								    const unsigned short int     initial_value )
{
	int result=0;
	errno=0; 

	/* Initialize semaphore object semaphore to initial_value.  If PSHARED then share it with other processes.  */
	result = sem_init (semaphore, 0, initial_value);	
	if (result == -1) 
	{
		//PASLOG_ERROR_2(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "OSSRV Linux counting_semaphore_init: Error, sem_init function failed: result %d, initial counting value %d\n", result, initial_value);
		return (EXIT_ERROR);
	}

	return (EXIT_OK);
}

int OSSRV_counting_semaphore_post ( OSSRV_counting_semaphore_t  *semaphore )
{
	int result=0;

	errno=0; 
	result = sem_post (semaphore);
	if (result == -1) 
	{
		//PASLOG_ERROR_1(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "OSSRV Linux counting_semaphore_post: Error, sem_post function failed: result %d\n", result);
		return (EXIT_ERROR);
	}

	return (EXIT_OK);
}

int OSSRV_counting_semaphore_wait ( OSSRV_counting_semaphore_t  *semaphore )
{
	int result=0;
	errno=0; 
	result = sem_wait (semaphore); /* Always return 0 */
	if (result == -1) 
	{
		//PASLOG_ERROR_1(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "OSSRV Linux counting_semaphore_post: Error, sem_post function failed: result %d\n", result);
		return (EXIT_ERROR);
	}
	return (EXIT_OK);

}

int OSSRV_counting_semaphore_trywait ( OSSRV_counting_semaphore_t  *semaphore )
{
	int result=0;
	errno = 0;
	result = sem_trywait (semaphore);
	
	if (result == 0) 
		return (1);
	else
	{ 
		if (errno != EAGAIN) 
		{
			char *error_str = NULL; 
			error_str = strerror (errno);
			//PASLOG_ERROR_3(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "OSSRV Linux counting_semaphore_trywait: Error %d on trywait(result = %d): %s\n", errno,result,error_str);

			return (EXIT_ERROR);
		}
		else return (0);
	}

	return (EXIT_OK);
}

int OSSRV_counting_semaphore_wait_timeout ( OSSRV_counting_semaphore_t  *semaphore,
                                            long int                     milliseconds   
                                          )
{
    struct timeval abs_timeout;
    struct timezone tz;
    timespec_t timeout;
    short int  result;


    /* Get current real time clock and add to it the waiting time */
    timeout.tv_sec  = 0;
    timeout.tv_nsec = 0;
    errno = 0; /* reset errno before error occurs */
    gettimeofday(&abs_timeout,&tz);

    /* [sem clock](sec) = [current clock](sec) + [user wait time, only sec] */
    timeout.tv_sec  = abs_timeout.tv_sec + (milliseconds/1000);
    /* [sem clock](nsec) = [current clock](nsec) + [user wait time, only milisec](nsec) */
    timeout.tv_nsec = (abs_timeout.tv_usec*1000) + ((milliseconds%1000)*1000000);

    /* [current clock](nsec) + [user wait time, less then sec](nsec) > 1sec(nsec)*/
    if ((timeout.tv_nsec) > 1000000000)
    {
        /* we have more then 1 sec in nano sec so inc the sec */
        timeout.tv_sec  += 1;
        timeout.tv_nsec -= 1000000000;
    }
    
	result = sem_timedwait (semaphore, &timeout);
    if (result == 0)
    {
        /* semaphore was signalled */
        return (EXIT_OK);
    }

    if (result > 0)
    {
        /* buggy glibc, copy the returned error code to errno, 
           where it should be */
        errno = result;
    }

    if (result < 0)
    {
        /* error code is in errno */
        if (errno == ETIMEDOUT) 
        {
            return (TIME_OUT);
        }
        
   		//PASLOG_ERROR_1(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "Error, sem_timedwait function failed: result %d\n", result);

		return (EXIT_ERROR);
    }

	return (EXIT_OK);

}

int OSSRV_counting_semaphore_close ( OSSRV_counting_semaphore_t  *semaphore )
{
	int result=0;
	errno=0; 

	result = sem_destroy (semaphore);
	if (result != 0) 
	{
		char *error_str = NULL; 
		error_str = strerror (errno);
		//PASLOG_ERROR_3(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "OSSRV Linux counting_semaphore_close: Error %d on closing semaphore(result = %d): %s\n", errno,result,error_str);

		return (EXIT_ERROR);
	}

	return (EXIT_OK);
}


/* Old OSSRV EXT commands */
int OSSRV_writev ( int  filedes, const struct iovec *iov, int count)
{
	int nbytes;
	
	nbytes = writev(filedes, iov, count);
 
	if (nbytes < 0)
	{
		//PASLOG_ERROR_1(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "OSSRV EXT Linux error writev %d\n",nbytes);
	}
	return nbytes;
}
char *OSSRV_getcwd ( char *buf, int len)
{
	char  *result;

	result = getcwd(buf, len);
	if (result < 0)
	{
		//PASLOG_ERROR_0(PASLOG_FUNC_GENERAL, PASLOG_OLT_IRRELEVANT, PASLOG_ONU_IRRELEVANT, "Failed to _etcwd\n");
	}
	return result;
}

int OSSRV_getpagesize(void)
{
	return getpagesize();
}


int OSSRV_snprintf (char *buf, size_t maxlen, const char *format, ...)
{
	int result;
	
	va_list ap;
	va_start (ap, format);
 	result = vsnprintf(buf, maxlen, format,ap); 
	va_end (ap);

    if (result > -1 && result < maxlen)
         return -1;

	return result;
}

char *OSSRV_strdup (const char *strSource)
{
	void *result;

	result = (char *)strdup(strSource);
	return result;
}


int OSSRV_gettimeofday (struct timeval * tv, struct timezone *tz)
{
 	return gettimeofday(tv, tz);
}
